package com.fl.nettydemo.handler;

import com.fl.nettydemo.agreement.DataReport;
import com.fl.nettydemo.agreement.HeartBeat;
import com.fl.nettydemo.agreement.StartFrame;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.swing.text.DateFormatter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 转译编码
 */
@Slf4j
@Component
public class TransCodingUntil {
    private static Map<Integer, String> map = new ConcurrentHashMap<>();
    //执行文件校验线程池
    private static ExecutorService es = Executors.newFixedThreadPool(2);

    private static ReentrantLock lock = new ReentrantLock();
    //当status等于0时执行计算收包数量方法，接收中为1
    private static boolean status = true;

    private static ChannelHandlerContext context = null;

    //转译心跳
    public static HeartBeat CodeByHeartBeat(List<String> code) {
        //字节长度 2
        List<String> len = code.subList(1, 3);
        //功能码 1
        Integer sign = Integer.valueOf(code.get(3), 16);
        //设备编码
        List<String> equipmentCodes = code.subList(4, 14);
        //拼接字符 转义出数据报长度
        Integer CodeLength = subcodedesc(len);
        String equipmentCode = subcode(equipmentCodes);
        HeartBeat heartBeat = new HeartBeat(null, equipmentCode, CodeLength, sign, null);
        return heartBeat;
    }

    //转译起始帧
    public static StartFrame CodeByStartFrame(List<String> code) {
        map = new ConcurrentHashMap<>();
        //字节长度 2
        List<String> len = code.subList(1, 3);
        //功能码 1
        Integer sign = Integer.valueOf(code.get(3), 16);
        //设备编码
        List<String> equipmentCodes = code.subList(4, 14);
        //采样率
        List<String> samplingRateList = code.subList(14, 16);
        //采样点
        List<String> samplingPointsList = code.subList(16, 20);
        //时间
        List<String> TimeMillis = code.subList(20, 44);

        //校验位
        List<String> CheckBitList = code.subList(44, 46);
        //拼接字符 转义出数据报长度
        Integer CodeLength = subcodedesc(len);
        String equipmentCode = subcode(equipmentCodes);
        String time = getTime(TimeMillis);
        Integer samplingRate = subcodedesc(samplingRateList);
        Integer samplingPoints = subcodedesc(samplingPointsList);
        Integer CheckBit = subcodedesc(CheckBitList);
        StartFrame frame = new StartFrame(null, equipmentCode, CodeLength, sign, null, samplingPoints, samplingRate, time, CheckBit);
        return frame;
    }

    //转译数据报1
    public static DataReport CodeByDataReport(List<String> code, ChannelHandlerContext ctx) {
        //传入当前通道对象
        context = ctx;
        //字节长度 2
        List<String> len = code.subList(1, 3);
        //功能码 1
        Integer sign = Integer.valueOf(code.get(3), 16);
        //设备编码
        List<String> equipmentCodes = code.subList(4, 14);
        // 相位序
        List<String> phaseSequences = code.subList(14, 15);
        //消息包总数
        List<String> PackageTotals = code.subList(15, 17);
        //消息包序号
        List<String> PackageNumbers = code.subList(17, 19);

        Integer CodeLength = subcodedesc(len);
        String equipmentCode = subcode(equipmentCodes);
        String phaseSequence = subcode(phaseSequences);
        Integer PackageTotal = subcodedesc(PackageTotals);
        Integer PackageNumber = subcodedesc(PackageNumbers);
        //校验位 1019 1021
        List<String> CheckBitList = code.subList(CodeLength + 1, CodeLength + 2);
        Integer CheckBit = subcodedesc(CheckBitList);
        //数据包
        List<String> datas = code.subList(19, CodeLength + 1);
        String data = getData(datas, PackageTotal, PackageNumber);
        DataReport dataReport = new DataReport(null, equipmentCode, CodeLength, sign, null, phaseSequence, PackageTotal, PackageNumber, data, CheckBit);
        return dataReport;
    }

    public static String getTime(List<String> time) {
        List<String> nss = time.subList(0, 2);
        List<String> uss = time.subList(2, 4);
        List<String> mss = time.subList(4, 6);
        List<String> m = time.subList(6, 7);
        Integer ns = subcodedesc(nss);
        Integer us = subcodedesc(uss);
        Integer ms = subcodedesc(mss);
        String m1 = subcode(m);
        String min = subcode(time.subList(7, 8));
        String hour = subcode(time.subList(8, 9));
        String day = subcode(time.subList(9, 10));
        String mouth = subcode(time.subList(10, 11));
        Integer year = subcodedesc(time.subList(11, 13));
        Instant now = Instant.now();
        System.out.println(now);
        String NowTime = year + "-" + mouth + "-" + day + " " + hour + ":" + min + ":" + m1 + ":" + ms + ":" + us + ":" + ns;
        log.info("{}年{}月{}日{}:{}:{}  {}毫秒:{}-微秒:{}-纳秒", year, mouth, day, hour, min, m1, ms, us, ns);
        return NowTime;
    }

    private static String subcode(List<String> codeSize) {
        List<String> list = new ArrayList<>();
        for (String cod : codeSize) {
            int val = Integer.valueOf(cod, 16);
            list.add(String.valueOf(val));
        }
        if (list.get(0).equals("0") && list.size() > 1) {
            list.remove(0);
        }
        String collect = list.stream().collect(Collectors.joining(""));
        return collect;
    }

    /**
     * 数据包 包总数 包序号
     *
     * @param codeSize
     * @param total
     * @param number
     * @return
     */
    @SneakyThrows
    private static String getData(List<String> codeSize, Integer total, Integer number) {
//
//        if (number == 5) {
//            System.out.println("模拟阻塞!");
//            TimeUnit.SECONDS.sleep(1);
//        }
        if (status) {
            status = false;
            new Thread(() -> {
                LocalDateTime before = LocalDateTime.now();
                while (map.size() < 10) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        long between = ChronoUnit.SECONDS.between(before, LocalDateTime.now());
                        System.out.println("正在计算接包数量...." + map.size() + "---" + between);
                        if (between > 2) {
                            System.out.println("获取包超时！");
                            System.out.println(context);
                            status = true;
                            break;
                        }

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                status = true;
            }).start();
        }
        lock.lock();
        List<String> list = new ArrayList<>();
        for (int i = 0; i < codeSize.size(); i += 2) {
            String val = codeSize.get(i + 1) + codeSize.get(i);
            Integer value = Integer.valueOf(val, 16);
            list.add(String.valueOf(value));
        }
        System.out.println("数据长度:" + list.size());
        String collect = list.stream().collect(Collectors.joining(","));
        int obj = SaveDataObj(collect, total, number);
        log.info("已存储集合数量:{}", obj);
        lock.unlock();
        return collect;
    }

    /**
     * 保存过电压数据到本地
     *
     * @param collect 16进制数据
     * @param total   数据总包数
     * @param number  包序号
     * @return
     */
    public static int SaveDataObj(String collect, Integer total, Integer number) throws IOException {
        map.put(number, collect);
        if (map.size() >= total) {
            System.out.println("存储到本地");
            String dest = "";
            for (int i = 0; i < map.size(); i++) {
                dest += map.get(i) + ",";
            }
            ByteBuffer buffer = StandardCharsets.UTF_8.encode(dest);
            FileChannel channel = null;
            try {
                channel = new RandomAccessFile("F:/DataFiles/" + DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss").format(LocalDateTime.now()), "rw").getChannel();
                channel.write(buffer);
                channel.close();
            } catch (Exception e) {
                channel.close();
                e.printStackTrace();
            } finally {
                channel.close();
            }
        }
        return map.size();
    }

    /**
     * 多字节数据传输格式： 低字节在前，高字节在后
     *
     * @param list
     * @return
     */
    public static Integer subcodedesc(List<String> list) {
        if (list != null) {
            String collect = null;
            Integer val = null;
            if (list.size() == 2 || list.size() == 4) {
                Collections.reverse(list);
                collect = list.stream().collect(Collectors.joining(""));
                val = Integer.valueOf(collect, 16);
                return val;
            }
            return null;
        } else {
            return null;
        }
    }

    public static Integer method(int threadNum, int timeOut) {
        // 获取线程池
        // Future用于执行多线程的执行结果
        Future<Integer> future = es.submit(() -> {
            LocalDateTime before = LocalDateTime.now();
            while (map.size() < 10) {
                long between = ChronoUnit.SECONDS.between(before, LocalDateTime.now());
                System.out.println("正在计算接包数量...." + map.size() + "--" + between);
                if (between > 2) {
                    System.out.println("获取包超时！");
                    break;
                }
            }
            return null;
        });

        try {
            // futrue.get()测试被执行的程序是否能在timeOut时限内返回字符串
            return future.get(timeOut, TimeUnit.SECONDS);//任务处理超时时间设为 1 秒
        } catch (TimeoutException ex) {
            System.out.println("获取数据超时：" + ex.getMessage());
            try {
                TimeUnit.SECONDS.sleep(timeOut);
                System.out.println("进行请求重发");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
